Skip to content

Comments

feat: S2 unavailable menu item#9657

Open
LFDanLu wants to merge 12 commits intomainfrom
s2_unavailable_menu_item
Open

feat: S2 unavailable menu item#9657
LFDanLu wants to merge 12 commits intomainfrom
s2_unavailable_menu_item

Conversation

@LFDanLu
Copy link
Member

@LFDanLu LFDanLu commented Feb 13, 2026

Closes

✅ Pull Request Checklist:

  • Included link to corresponding React Spectrum GitHub Issue.
  • Added/updated unit tests and storybook for this change (for new code or code which already has tests).
  • Filled out test instructions.
  • Updated documentation (if it already exists for this component).
  • Looked at the Accessibility Practices for this feature - Aria Practices

📝 Test Instructions:

Test both the S2 and RAC implementations in both storybook and docs.

🧢 Your Project:

RSP

@rspbot
Copy link

rspbot commented Feb 13, 2026

@rspbot
Copy link

rspbot commented Feb 17, 2026


### Unavailable menu items

Wrap a `<MenuItem>` and a `<Popover>` with a `<UnavailableMenuItemTrigger>` and apply `isUnavailable` to disable the item's default action and show an informational popover instead.
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I opted to have put the Popover directly into the example rather than create a UnavailableMenuItemTrigger wrapper in the starters since I felt it might be confusing to have Popover mentioned here but not show up in the example. Open to opinions though, not too bothered if we wanna go the same route as SubmenuTrigger and wrap the Popover in the starters


let descriptor = style({
gridArea: 'descriptor',
// TODO: currently the unavailable icon is misaligned with the submenu arrow, check with spectrum
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

still waiting for feedback but feel free to weigh in

Comment on lines +655 to +658
// TODO: consider if we should instead pull the RAC defined trigger here into S2. Feels useful for RAC users though, but
// maybe shouldn't be specific to unavailable. It is very similar to SubmenuTrigger but it renders a dialog type behavior (see useSubmenuTrigger)
// and has the concept of isUnavailable which will make the item render normally when false
function UnavailableMenuItemTrigger(props: UnavailableMenuItemTriggerProps): JSX.Element {
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

UnavailableMenuItemTrigger seems a bit specific in RAC honestly, open to opinions if there should be a more generic naming/component

@LFDanLu LFDanLu changed the title feat: (WIP) S2 unavailable menu item feat: S2 unavailable menu item Feb 17, 2026
@rspbot
Copy link

rspbot commented Feb 17, 2026

@LFDanLu LFDanLu marked this pull request as ready for review February 18, 2026 18:49
Comment on lines +36 to +39
// TODO: this is quite specific, but description and keyboardShortcut above are also very specific
/** Props for the descriptive element in the end slot inside the menu item (e.g. info icon, chevron). */
endSlotProps: DOMAttributes,

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm quite iffy about this, as stated above it is very specific. I think ideally we'd give the user a way to make arbitrary child elements in a MenuItem part of that MenuItem's aria-describedby. I could've opted to just allow the user to provide a generic aria-describedBy to the menu item and then apply ids to various other elements but we already have description and keyboard shortcut handled so figured we may be able to make end slot a thing too

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also note that due to how collection items work in RAC (aka UnavailableMenuItemTrigger in RAC is a collection branch that only renders the popover child as is), I believe that RAC level change is unavoidable since there isn't a good way to generate a id at the S2 UnavailableMenuItemTrigger level and propagate it down via context

@rspbot
Copy link

rspbot commented Feb 19, 2026

@rspbot
Copy link

rspbot commented Feb 20, 2026

reidbarber
reidbarber previously approved these changes Feb 20, 2026
Copy link
Member

@reidbarber reidbarber left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

* A unavailable menu item trigger is used to wrap a unavailable menu item. When the menu item is unavailable,
* selecting it opens the wrapped Popover instead of performing the item's action.
*
* @version alpha
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we need alpha badges in the docs?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

good call, added. I've opted not to add it for S2 since I think it is fairly simple

@rspbot
Copy link

rspbot commented Feb 20, 2026

@rspbot
Copy link

rspbot commented Feb 20, 2026

## API Changes

react-aria-components

/react-aria-components:MenuItemRenderProps

 MenuItemRenderProps {
   allowsDragging?: boolean
   hasSubmenu: boolean
   isDisabled: boolean
   isDragging?: boolean
   isDropTarget?: boolean
   isFocusVisible: boolean
   isFocused: boolean
   isHovered: boolean
   isOpen: boolean
   isPressed: boolean
   isSelected: boolean
+  isUnavailable?: boolean
   selectionBehavior: SelectionBehavior
   selectionMode: SelectionMode
 }

/react-aria-components:UnavailableMenuItemTrigger

+UnavailableMenuItemTrigger {
+  children: Array<ReactElement>
+  isUnavailable?: boolean = false
+}

/react-aria-components:EndSlotContext

+EndSlotContext {
+  UNTYPED
+}

/react-aria-components:UnavailableMenuItemTriggerProps

+UnavailableMenuItemTriggerProps {
+  children: Array<ReactElement>
+  isUnavailable?: boolean = false
+}

@react-aria/menu

/@react-aria/menu:MenuItemAria

 MenuItemAria {
   descriptionProps: DOMAttributes
+  endSlotProps: DOMAttributes
   isDisabled: boolean
   isFocusVisible: boolean
   isFocused: boolean
   isPressed: boolean
   keyboardShortcutProps: DOMAttributes
   labelProps: DOMAttributes
   menuItemProps: DOMAttributes
 }

@react-spectrum/s2

/@react-spectrum/s2:UnavailableMenuItemTrigger

+UnavailableMenuItemTrigger {
+  children: Array<ReactElement>
+  isUnavailable?: boolean = false
+}

/@react-spectrum/s2:UnavailableMenuItemTriggerProps

+UnavailableMenuItemTriggerProps {
+  children: Array<ReactElement>
+  isUnavailable?: boolean = false
+}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants